Spring Cache 源码学习,自定义CacheManager
Spring Cache 源码学习,自定义CacheManager 一、学习背景 最近一直在学习Spring boot的框架,因为之前一直接触的都是Spring + Spring mvc + mybatis的ssm框架,对于约定大于配置的这种开发理念还是不太熟悉和上手,所以决定系统学习下Springboot。最近在看项目代码,对代码中Spring cache的一些自定义拓展很感兴趣,所以就好好整理下。(基于Spring 5.2.6-release 版本)
二、Spring Cache的源码学习 2.1、Spring Cache的核心接口 2.1.1、Cache 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 package org.springframework.cache;import java.util.concurrent.Callable;import org.springframework.lang.Nullable;public interface Cache { String getName () ; Object getNativeCache () ; @Nullable ValueWrapper get (Object key) ; @Nullable <T> T get (Object key, @Nullable Class<T> type) ; @Nullable <T> T get (Object key, Callable<T> valueLoader) ; void put (Object key, @Nullable Object value) ; @Nullable default ValueWrapper putIfAbsent (Object key, @Nullable Object value) { ValueWrapper existingValue = get(key); if (existingValue == null ) { put(key, value); } return existingValue; } void evict (Object key) ; default boolean evictIfPresent (Object key) { evict(key); return false ; } void clear () ; default boolean invalidate () { clear(); return false ; } @FunctionalInterface interface ValueWrapper { @Nullable Object get () ; } @SuppressWarnings("serial") class ValueRetrievalException extends RuntimeException { @Nullable private final Object key; public ValueRetrievalException (@Nullable Object key, Callable<?> loader, Throwable ex) { super (String.format("Value for key '%s' could not be loaded using '%s'" , key, loader), ex); this .key = key; } @Nullable public Object getKey () { return this .key; } } }
2.1.2、CacheManager 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package org.springframework.cache;import java.util.Collection;import org.springframework.lang.Nullable;public interface CacheManager { @Nullable Cache getCache (String name) ; Collection<String> getCacheNames () ; }
2.1.3、Cache相关的注解 Spring cache 为了方便我们对缓存的使用,提供了五个比较有用的注解:
@Cacheable @CachePut @CacheEvict @Caching @CacheConfig
2.1.3.1、@Cacheable 源码注释翻译:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 package org.springframework.cache.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import java.util.concurrent.Callable;import org.springframework.core.annotation.AliasFor;@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Cacheable { @AliasFor("cacheNames") String[] value() default {}; @AliasFor("value") String[] cacheNames() default {}; String key () default "" ; String keyGenerator () default "" ; String cacheManager () default "" ; String cacheResolver () default "" ; String condition () default "" ; String unless () default "" ; boolean sync () default false ; }
主要参数
描述
用法
value
cacheNames 的别名
@Cacheable(“mysqlCache”)
cacheNames
缓存的名称
@Cacheable(cacheNames = MysqlCache.NAME)
key
缓存的key,支持Spring EL 表达式
@Cacheable(cacheNames = MysqlCache.NAME, key = “‘CacheDemoService-get’+ args[0]”) @Cacheable(cacheNames = MysqlCache.NAME, key = “‘CacheDemoService-get’+ #p0”)
@Cacheable(cacheNames = MysqlCache.NAME, key = “‘CacheDemoService-get’+ #a0”)
keyGenerator
key的构造器,和key属性互斥
需要定义一个customerKeyGenerator的bean @Cacheable(cacheNames = MysqlCache.NAME, keyGenerator = “customerKeyGenerator”)
cacheManager
指定CacheManager,需要先定义CacheManager的bean,指定后则不能指定cacheResolver,两者互斥,如不指定,则使用注入的默认的CacheManagerBean
@Cacheable(cacheNames = MysqlCache.NAME, cacheManager=”mysqlCacheManager”,keyGenerator = “customerKeyGenerator”)
cacheResolver
指定cacheResolver的bean的名称,和cacheManager互斥
暂时未做测试
condition
是否使用方法缓存条件,默认“”,是否去取方法缓存,在方法执行之前计算条件结果
@Cacheable(cacheNames = MysqlCache.NAME, cacheManager=”mysqlCacheManager”,keyGenerator = “customerKeyGenerator”,condition=“#a0 == ‘xx’”)
unless
否决方法的结果缓存,默认“”,始终缓存方法,如果条件不成立则不缓存方法结果,在方法执行结果后计算条件结果
@Cacheable(cacheNames = MysqlCache.NAME, cacheManager=”mysqlCacheManager”,keyGenerator = “customerKeyGenerator”,unless=“#a0 == ‘xx’”)
sync
多线程访问方法,是否同步缓存,默认值为false,使用有三个限制,不支持unless,只能指定一个缓存,不能与@Caching一起使用
@Cacheable(cacheNames = MysqlCache.NAME, cacheManager=”mysqlCacheManager”,keyGenerator = “customerKeyGenerator”,sync=false)
2.1.3.2、@CachePut 源码注释翻译:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 package org.springframework.cache.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.core.annotation.AliasFor;@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface CachePut { @AliasFor("cacheNames") String[] value() default {}; @AliasFor("value") String[] cacheNames() default {}; String key () default "" ; String keyGenerator () default "" ; String cacheManager () default "" ; String cacheResolver () default "" ; String condition () default "" ; String unless () default "" ; }
主要参数
描述
注释
value
同Cacheable
@CachePut(“mysqlCache”)
cacheNames
同Cacheable
@CachePut(cacheNames=”mysqlCache”)
key
同Cacheable
@CachePut(cacheNames=”mysqlCache”,key=”#a1”)
keyGenerator
同Cacheable
@CachePut(cacheNames=”mysqlCache”,keyGenerator=”customKeyGenerator”)
cacheManager
同Cacheable
@CachePut(cacheNames = MysqlCache.NAME, cacheManager=”mysqlCacheManager”,keyGenerator = “customerKeyGenerator”)
cacheResolver()
同Cacheable
暂未测试
condition
同Cacheable
@CachePut(cacheNames = MysqlCache.NAME, cacheManager=”mysqlCacheManager”,keyGenerator = “customerKeyGenerator”,condition=”#a1 == ‘xxx’”)
unless
同Cacheable
@CachePut(cacheNames = MysqlCache.NAME, cacheManager=”mysqlCacheManager”,keyGenerator = “customerKeyGenerator”,unless=”#a1 == ‘xxx’”)
2.1.3.3、@CacheEvict 源码注释翻译:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 package org.springframework.cache.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.core.annotation.AliasFor;@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface CacheEvict { @AliasFor("cacheNames") String[] value() default {}; @AliasFor("value") String[] cacheNames() default {}; String key () default "" ; String keyGenerator () default "" ; String cacheManager () default "" ; String cacheResolver () default "" ; String condition () default "" ; boolean allEntries () default false ; boolean beforeInvocation () default false ; }
主要参数
描述
用法
value
同Cacheable
@CacheEvict(“mysqlCache”)
cacheNames
同Cacheable
@CacheEvict(cacheNames=”mysqlCache”)
key
同Cacheable
@CacheEvict(cacheNames=”mysqlCache”,key=”#a1”)
keyGenerator
同Cacheable
@CacheEvict(cacheNames=”mysqlCache”,key=”customKeyGenerator”)
cacheManager
同Cacheable
@CacheEvict(cacheNames=”mysqlCache”,key=”customKeyGenerator”,cacheManager=”customCacheManager”)
cacheResolver
同Cacheable
暂时未测试
condition
同Cacheable
@CacheEvict(cacheNames=”mysqlCache”,key=”#a1”,condition=”#a1 == true”)
unless
同Cacheable
@CacheEvict(cacheNames=”mysqlCache”,key=”#a1”,unless=”#a1 == true”)
allEntries
是否删除该缓存实例下的所有缓存的信息,默认值:false,当值为true时,则与属性key互斥
@CacheEvict(cacheNames=”mysqlCache”,allEntries = false
beforeInvocation
在调用该方法之前是否移除缓存,默认为false,如果为true时,则移除缓存和方法是否执行异常无关
@CacheEvict(cacheNames=”mysqlCache”,key=”#a1”,unless=”#a1 == true”,beforeInvocation = true)
2.1.3.4、@Caching 源码注释翻译:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package org.springframework.cache.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Caching { Cacheable[] cacheable() default {}; CachePut[] put() default {}; CacheEvict[] evict() default {}; }
2.1.3.5、@CacheConfig 源码注释翻译:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package org.springframework.cache.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CacheConfig { String[] cacheNames() default {}; String keyGenerator () default "" ; String cacheManager () default "" ; String cacheResolver () default "" ; }
该注解只能使用在类上,为该类中的@Cacheable、@CachePut、@CacheEvict提供cacheNames、cacheManager、keyGenerator、cacheResolver默认的设置。不难发现这些值都是Spring 的bean的实例名称。
主要参数
描述
用法
cacheNames
缓存名称的bean的名称,默认值:“”,则以实际的设置为准
@CacheConfig(cacheNames=”mysqlCache”)
cacheManager
缓存管理器的bean的名称,默认值:“”,则以实际的设置为准
@CacheConfig(cacheManager=”customCacheManager”)
keyGenerator
key生成器的bean的名称,默认值:“”,则以实际的设置为准
@CacheConfig(keyGenerator=”customKeyGnenrator”)
cacheResolver
缓存实现的bean的名称,默认值:“”,则以实际的设置为准
@CacheConfig(cacheResolver=”customCacheResolver”)
三、Spring Cache的实现原理 3.1、基本实现原理 Spring的缓存实现,依赖于Spring AOP,通过获取上面讲述的五个注解的切点位置,来实现对应的功能。 那么意味着如果要使用缓存或者缓存生效前提,所有的缓存方法调用实例都必须是Spring 代理的Bean。否则的话就无法使用。
3.2、核心实现类 Spring Cache的核心类的依赖关系图:
Spring Cache的执行逻辑:
3.3、Spring Cache 注解失效的场景 在开发的过程中,可能会发现缓存没有生效,原因可能如下:
1、方法所在的Bean没有注入到Spring的容器 2、方法是私有的或者是final修饰的 3、方法的调用对象为非Spring的代理类,类似于this.method()方式调用
四、自定义Spring Cache 在学习的过程中,做了一个简单的小demo拓展了一个Cache方式,尝试将缓存存储到Mysql的数据表中。
4.1、准备阶段
技术选型
版本
java
1.8.0_241
Spring boot
2.3.0.release
Spring jpa
2.3.0.release
数据库
Mysql 5.x
Spring boot test
2.3.0.release
实体类加强工具
lombok
4.2、创建缓存表的CRUD 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 package com.yiyi.cache.cache.entity;import lombok.Data;import javax.persistence.*;import java.time.LocalDateTime;@Data @Entity(name = "t_cache") public class CacheObject { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(name = "cache_key",nullable = false) private String cacheKey; @Column(name = "cache_value",nullable = false) private String cacheValue; @Column(name = "create_date",nullable = false) private LocalDateTime createDate; } package com.yiyi.cache.cache.entity;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.Modifying;import org.springframework.transaction.annotation.Transactional;public interface CacheObjectMapper extends JpaRepository <CacheObject ,String > { CacheObject getByCacheKey (String cacheKey) ; @Modifying @Transactional void deleteCacheObjectByCacheKey (String cacheKey) ; } package com.yiyi.cache.cache.entity;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.SneakyThrows;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;import java.time.LocalDateTime;@Service public class CacheObjectService { @Resource private CacheObjectMapper cacheObjectMapper; public String getValue (String key) { CacheObject cacheObject = cacheObjectMapper.getByCacheKey(key); if (cacheObject == null ){ return null ; } return cacheObject.getCacheValue(); } @SneakyThrows @Transactional(rollbackFor = Exception.class) public void putValue (String key, String value) { CacheObject oldCacheObject = cacheObjectMapper.getByCacheKey(key); if (oldCacheObject != null ){ cacheObjectMapper.delete(oldCacheObject); } CacheObject cacheObject = new CacheObject(); cacheObject.setCacheKey(key); ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(value); cacheObject.setCacheValue(json); cacheObject.setCreateDate(LocalDateTime.now()); cacheObjectMapper.save(cacheObject); } public void evict (String key) { cacheObjectMapper.deleteCacheObjectByCacheKey(key); } @Transactional public void clear () { cacheObjectMapper.deleteAll(); } }
4.3、Spring Cache的拓展 4.3.1、定义CacheManager 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.yiyi.cache.cache.manager;import org.springframework.cache.Cache;import org.springframework.cache.CacheManager;import java.util.Collection;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;public class CustomCacheManager implements CacheManager { private final static ConcurrentMap<String,Cache> cacheMap = new ConcurrentHashMap<>(); @Override public Cache getCache (String name) { return cacheMap.get(name); } @Override public Collection<String> getCacheNames () { return cacheMap.keySet(); } public void createCache (Cache cache) { cacheMap.put(cache.getName(),cache); } }
4.3.2、定义Cache 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 package com.yiyi.cache.cache.manager;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.SerializerProvider;import com.yiyi.cache.cache.entity.CacheObjectService;import com.yiyi.cache.cache.serializer.CustomSerializerImpl;import lombok.SneakyThrows;import org.springframework.beans.BeansException;import org.springframework.cache.Cache;import org.springframework.cache.support.SimpleValueWrapper;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;import java.util.concurrent.Callable;@Component public class MysqlCache implements Cache , ApplicationContextAware { public final static String NAME = "MYSQL" ; private ApplicationContext applicationContext; @Override public String getName () { return NAME; } @Override public Object getNativeCache () { return this ; } @Override public ValueWrapper get (Object key) { String value = applicationContext.getBean(CacheObjectService.class).getValue(key.toString()); return value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null ; } @SneakyThrows @Override public <T> T get (Object key, Class<T> type) { String value = applicationContext.getBean(CacheObjectService.class).getValue(key.toString()); return (T) fromStoreValue(value); } @SneakyThrows @Override public <T> T get (Object key, Callable<T> valueLoader) { String value = applicationContext.getBean(CacheObjectService.class).getValue(key.toString()); return (T) fromStoreValue(value); } @Override public void put (Object key, Object value) { String cacheValue = toStoreValue(value); applicationContext.getBean(CacheObjectService.class).putValue(key.toString(), cacheValue); } @Override public void evict (Object key) { applicationContext.getBean(CacheObjectService.class).evict(key.toString()); } @Override public void clear () { applicationContext.getBean(CacheObjectService.class).clear(); } @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { this .applicationContext = applicationContext; } @SneakyThrows private String toStoreValue (Object value) { CustomSerializerImpl customSerializer = new CustomSerializerImpl(); return customSerializer.serialize(value); } @SneakyThrows private Object fromStoreValue (String value) { CustomSerializerImpl customSerializer = new CustomSerializerImpl(); return customSerializer.deserialize(value); } }
4.3.3、配置Cache 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.yiyi.cache.cache.config;import com.yiyi.cache.cache.manager.CustomCacheManager;import com.yiyi.cache.cache.manager.MysqlCache;import org.springframework.cache.CacheManager;import org.springframework.cache.concurrent.ConcurrentMapCache;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;@Configuration public class CacheConfig { @Resource private MysqlCache mysqlCache; @Bean public CacheManager cacheManager () { CustomCacheManager customCacheManager = new CustomCacheManager(); customCacheManager.createCache(mysqlCache); ConcurrentMapCache local = new ConcurrentMapCache("LOCAL" ); customCacheManager.createCache(local); return customCacheManager; } }
4.4.4、Cache的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.yiyi.cache.cache.service;import com.yiyi.cache.cache.entity.User;import com.yiyi.cache.cache.manager.MysqlCache;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.Cacheable;import org.springframework.cache.annotation.Caching;import org.springframework.stereotype.Service;@Service public class CacheDemoService { @Cacheable(cacheNames = MysqlCache.NAME, key = "'CacheDemoService-get'+ args[0]") public String get (String name) { return name; } @Cacheable(cacheNames = MysqlCache.NAME, keyGenerator = "customKeyGenerator") public User keyGenerator (String name, String value) { User user = new User(); user.setUsername(name+value); return user; } @Cacheable(cacheNames = MysqlCache.NAME, key = "'CacheDemoService-getUser'+ args[0]") public User getUser (String name) { User user = new User(); user.setUsername(name); return user; } @Cacheable(cacheNames = "LOCAL", key = "'CacheDemoService-get'+ args[0]") public String getLocalCache (String name) { return name; } @Caching(evict = { @CacheEvict(cacheNames = {MysqlCache.NAME}, key = "'CacheDemoService-get'+ args[0]"), @CacheEvict(cacheNames = MysqlCache.NAME, key = "'CacheDemoService-getUser'+ args[0]") }) public void clearCache (String name) { } }
测试用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import com.yiyi.cache.SpringCacheApplication;import com.yiyi.cache.cache.entity.User;import com.yiyi.cache.cache.service.CacheDemoService;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;@SpringBootTest(classes = {SpringCacheApplication.class}) public class CacheDemoServiceTest { @Resource private CacheDemoService cacheDemoService; @Test public void testGet () { String name = "1231231" ; String s = cacheDemoService.get(name); System.out.println(s); System.out.println(cacheDemoService.get("1231" ).equals("1231" )); } @Test public void testGetUser () { String name = "1231231" ; User user = cacheDemoService.getUser(name); System.out.println(user.getUsername().equals(name)); } @Test public void testGetLocalCache () { String name = "1231231" ; String s = cacheDemoService.getLocalCache(name); System.out.println(s); System.out.println(cacheDemoService.getLocalCache("1231" ).equals("1231" )); } @Test public void testClearCache () { cacheDemoService.clearCache("1231231" ); } @Test public void testKeyGenerator () { User wuxuan = cacheDemoService.keyGenerator("wuxuan" , "123456" ); System.out.println(wuxuan); } }
五、总结 通过阅读Spring Cache的源码后,感觉收获到了很多,首先从心理上认识到,源码也并没有那么的难。其次,Spring 能够受到广大Java开发人员的追捧也并非偶然,Spring的源码从思路上以及设计上还是非常的优秀的,从中能够学到很多。以后再接再厉,多多尝试Discover Why 而不 Ask Why。